package net.minject.reflection;

import static com.google.common.collect.Lists.newArrayList;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

@SuppressWarnings("unchecked")
public class ReflectorUtils {

    public static <T> T construct(Class<T> c, Object... initargs) {
        Class<?>[] parameterTypes = new Class[initargs.length];
        for (int i = 0; i < initargs.length; i++) {
            parameterTypes[i] = initargs[i].getClass();
        }
        try {
            return c.getConstructor(parameterTypes).newInstance(initargs);
        } catch (Exception e) {
            throw handleReflectionException(e);
        }
    }
    
    public static <T> Constructor<T> findConstructorAnnnotatedBy(Class<T> clazz, Class<? extends Annotation> annotationClass) {
        for (Constructor<?> c : clazz.getConstructors()) {
            if (c.isAnnotationPresent(annotationClass)) {
                return (Constructor<T>) c;
            }
        }
        return null;
    }
    
    public static List<Field> findFieldsAnnotatedBy(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        List<Field> fields = newArrayList();
        for (Field f : clazz.getDeclaredFields()) {
            if (f.isAnnotationPresent(annotationClass)) {
                fields.add(f);
            }
        }
        return fields;
    }
    
    public static List<Method> findMethodsAnnotatedBy(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        List<Method> methods = newArrayList();
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.isAnnotationPresent(annotationClass)) {
                methods.add(m);
            }
        }
        return methods;
    }
    
    public static void setField(Field field, Object object, Object value) {
        try {
            field.setAccessible(true);
            field.set(object, value);
        } catch (Exception e) {
            throw handleReflectionException(e);
        }
    }
    
    public static Object invokeMethod(Method method, Object obj, Object... args) {
        try {
            method.setAccessible(true);
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw handleReflectionException(e);
        }
        
    }

    private static ReflectionException handleReflectionException(Exception e) {
        return new ReflectionException(e);
    }
}
